package com.pig4cloud.pigx.contract.mq.listener;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.contract.config.contract.ContractConfig;
import com.pig4cloud.pigx.contract.constant.ContractMqConstants;
import com.pig4cloud.pigx.contract.dto.ContractPositionNeedAppendMarginEmailDTO;
import com.pig4cloud.pigx.contract.entity.ContractPosition;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.contract.enums.ContractPositionEnum;
import com.pig4cloud.pigx.contract.executer.order.supporter.OrderCalculator;
import com.pig4cloud.pigx.contract.executer.position.supporter.PositionCalculator;
import com.pig4cloud.pigx.contract.mapper.ContractPositionMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 仓位检查保证金
 *
 * @since 2022/9/16 15:58
 */
@Slf4j
@Component
@AllArgsConstructor
public class PositionMarginCheckTriggerListener {

    private final ContractPositionMapper contractPositionMapper;

    private final ContractUtils contractUtils;
    private final CommonUtil commonUtil;
    private final MysqlUtil mysqlUtil;
    private final ContractConfig contractConfig;

    /**
     * 监听 contract.position.margin.check.trigger 队列，开始处理仓位检查保证金
     *
     * @param userId 用户id
     * @since 2022/9/16 16:00
     */
    @RabbitListener(queues = ContractMqConstants.QUEUE_FANOUT_CONTRACT_POSITION_MARGIN_CHECK_TRIGGER)
    public void run(Long userId) {
        log.info("{} 开始处理仓位检查保证金。msg：{}", this.getClass().getSimpleName(), userId);

        if (Objects.isNull(userId)) {
            return;
        }

        // 根据用户id查询所有未发送过邮件的可用仓位信息
        ContractPosition condition = new ContractPosition();
        condition.setUserId(userId);
        condition.setMarginCheckStatus(ContractPositionEnum.MarginCheckStatus.NORMAL.getValue());
        List<ContractPosition> list = contractUtils.getAllAvailablePosition(condition, false);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }

        // 计算并检查保证金率
        check(userId, list);
    }

    /**
     * 计算并检查保证金率
     *
     * @param userId 用户id
     * @param list   集合
     * @since 2022/9/17 19:16
     */
    public void check(Long userId, List<ContractPosition> list) {
        // 获取保证金率邮件触发值
        BigDecimal marginRateEmailTrigger = contractConfig.getPosition().getMarginRateEmailTrigger();

        // 遍历计算并处理
        for (ContractPosition i : list) {
            try {
                BigDecimal marginRate;
                // 全仓直接读取保证金率
                if (ContractPositionEnum.Mode.FULL.getValue().equals(i.getMode())) {
                    marginRate = i.getMarginRate();

                    // 逐仓计算保证金率
                } else {
                    // 获取币对信息
                    ContractSymbolMongo symbol = contractUtils.getSymbolFromThread(i.getSymbolName());
                    ContractUtils.checkSymbol(symbol);
                    // 未实现盈亏
                    BigDecimal profit = OrderCalculator.getProfit(symbol.getMarkPrice(), i.getOpenAvgPrice(), i.getNowNum(), i.getSide());
                    // 持仓价值
                    BigDecimal positionValue = OrderCalculator.getOrderValue(i.getOpenAvgPrice(), i.getNowNum());
                    // 标准保证金
                    BigDecimal standardMargin = PositionCalculator.getStandardMargin(positionValue, i.getMultiple());
                    // 有效保证金
                    BigDecimal effectiveMargin = PositionCalculator.getEffectiveMargin(i.getOpenAvgPrice(), i.getLiquidatePrice(), i.getSide(), i.getNowNum());
                    // 计算保证金率
                    marginRate = PositionCalculator.getMarginRate(effectiveMargin, profit, standardMargin);
                }

                // 如果小于等于0，不做处理，由强平引擎处理
                if (marginRate.compareTo(BigDecimal.ZERO) <= 0) {
                    continue;
                }

                // 判断保证金率是否低于等于触发值
                boolean isLower = marginRate.compareTo(marginRateEmailTrigger) <= 0;
                if (!isLower) {
                    continue;
                }

                // 尝试修改保证金检查状态
                UpdateWrapper<ContractPosition> update = new UpdateWrapper<>();
                update.lambda()
                        .set(ContractPosition::getMarginCheckStatus, ContractPositionEnum.MarginCheckStatus.BEEN_SENT.getValue())
                        .eq(ContractPosition::getId, i.getId())
                        .eq(ContractPosition::getNeedLiquidate, CommonDictEnum.YesOrNo.NO.getValue())
                        .in(ContractPosition::getStatus, ContractOrderEnum.Status.ON_GOING.getValue(),
                                ContractOrderEnum.Status.PARTIAL_DEAL.getValue())
                        .eq(ContractPosition::getMarginCheckStatus, ContractPositionEnum.MarginCheckStatus.NORMAL.getValue());

                // 开启本地事务
                mysqlUtil.startLocalTx();
                if (contractPositionMapper.update(null, update) != 1) {
                    // 回滚本地事务
                    mysqlUtil.commitLocalTx();
                    continue;
                }

                // 提交本地事务
                mysqlUtil.commitLocalTx();

                // 修改成功，组装邮件参数
                ContractPositionNeedAppendMarginEmailDTO emailDto = new ContractPositionNeedAppendMarginEmailDTO();
                emailDto.setUserId(userId);
                emailDto.setSymbolName(i.getSymbolName());
                emailDto.setPositionMode(i.getMode());
                emailDto.setSide(i.getSide());
                emailDto.setMultiple(i.getMultiple());

                // 发送邮件
                contractUtils.sendNeedAppendMarginEmailMq(Collections.singletonList(emailDto));

            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        // 清空threadLocal币对缓存
        ContractUtils.removeSymbolThreadCache();
    }
}
